<template>
    <div class="container">
        <div v-if="this.board && this.board.status !== 'gaming'" class="counter">{{ counter }}</div>
        <canvas ref="canvas" class="canvas" :width="canvasSize" :height="canvasSize"></canvas>
    </div>
</template>
<script>
    import {mixin, utils, events} from 'board-game/client';

    import { game as config } from '../config';

    export default {
        name: "game",
        mixins: [mixin],
        data() {
            return {
                vmin: utils.vmin,
                requireId: false,
                direction: {
                    up: false,
                    down: false,
                    left: false,
                    right: false
                }
            };
        },
        mounted() {
            this.requireId = window.requestAnimationFrame(() => this.draw());
        },
        beforeDestroy() {
            if (this.requireId !== false) {
                window.cancelAnimationFrame(this.requireId);
            }
        },
        computed: {
            canvasSize() {
                return this.vmin;
            },
            counter() {
                return this.board.status === 'waiting' ? 'WAIT' : Math.ceil((config.tick.count * config.tick.fps - (this.board.tick)) / config.tick.fps);
            },
        },
        methods: {
            draw() {
                if (this.board && this.board.map && this.role && this.role.location) {
                    const ts = +Date.now();
                    const used = [];

                    const map = this.board.map;
                    const shown = this.role.shown;
                    const drawer = this.$refs.canvas.getContext('2d');
                    const size = map.length;
                    const location = this.role.location;
                    const halfSize = Math.ceil(this.canvasSize / 2);
                    const tailSize = Math.ceil(this.canvasSize / config.render.size / 16) * 16;
                    const tailHalfSize = tailSize / 2;
                    const eyeLocation = tailHalfSize / 2;
                    const eyeSize = eyeLocation / 2;
                    const eyeRadius = eyeSize / 2;
                    const rx = location.x + location.dx / config.tick.step;
                    const ry = location.y + location.dy / config.tick.step;
                    const centerX = Math.max(Math.min(halfSize, rx * tailSize + tailHalfSize), this.canvasSize - (size - rx) * tailSize + tailHalfSize);
                    const centerY = Math.max(Math.min(halfSize, ry * tailSize + tailHalfSize), this.canvasSize - (size - ry) * tailSize + tailHalfSize);
                    const rescuers = this.board.rescuers.map(rescuer => {
                        const dx = rescuer.x + rescuer.dx / config.tick.step - (location.x + location.dx / config.tick.step);
                        const dy = rescuer.y + rescuer.dy / config.tick.step - (location.y + location.dy / config.tick.step);
                        const x = Math.round(centerX + dx * tailSize);
                        const y = Math.round(centerY + dy * tailSize);
                        return { dx, dy, x, y };
                    });
                    const minX = Math.max(location.x - Math.ceil(centerX / tailSize), 0);
                    const maxX = Math.min(minX + config.render.size + 1, size);
                    const minY = Math.max(location.y - Math.ceil(centerY / tailSize), 0);
                    const maxY = Math.min(minY + config.render.size + 1, size);

                    drawer.clearRect(0, 0, this.canvasSize, this.canvasSize);

                    const lxs = Math.round(centerX - tailHalfSize + (minX - rx) * tailSize);
                    const lys = Math.round(centerY - tailHalfSize + (minY - ry) * tailSize);
                    let color = '';
                    for (let y = minY, ly = lys; y < maxY; y++, ly += tailSize) {
                        const line = map[y];
                        for (let x = minX, lx = lxs; x < maxX; x++, lx += tailSize) {
                            if (line[x] === 1) {
                                if (color !== '#666') {
                                    drawer.fillStyle = color = '#666';
                                }
                            } else {
                                if (color !== '#FFF') {
                                    drawer.fillStyle = color = '#FFF';
                                }
                            }
                            drawer.fillRect(lx, ly, tailSize, tailSize);
                        }
                    }

                    used.push(+Date.now() - ts);

                    for (const {x, y} of rescuers) {
                        drawer.fillStyle = '#FF6';
                        drawer.beginPath();
                        drawer.arc(x, y, tailHalfSize, 0, Math.PI * 2, true);
                        drawer.fill();
                    }

                    used.push(+Date.now() - ts);

                    for (const victim of this.board.victims) {
                        if (victim.overTick === 0 && victim.playerId !== this.role.player.id) {
                            if (victim.group === this.role.group) {
                                drawer.fillStyle = '#00F';
                            } else {
                                drawer.fillStyle = '#F00';
                            }
                            const dx = victim.location.x + victim.location.dx / config.tick.step - rx;
                            const dy = victim.location.y + victim.location.dy / config.tick.step - ry;
                            const x = Math.round(centerX + dx * tailSize);
                            const y = Math.round(centerY + dy * tailSize);
                            drawer.beginPath();
                            drawer.arc(x, y, tailHalfSize, 0, Math.PI * 2, true);
                            drawer.fill();
                            drawer.fillStyle = '#333';
                            drawer.beginPath();
                            if (victim.face === 'up') {
                                drawer.arc(x - eyeLocation, y - eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                                drawer.arc(x + eyeLocation, y - eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                            } else if (victim.face === 'down') {
                                drawer.arc(x - eyeLocation, y + eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                                drawer.arc(x + eyeLocation, y + eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                            } else if (victim.face === 'right') {
                                drawer.arc(x + eyeLocation, y - eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                                drawer.arc(x + eyeLocation, y + eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                            } else if (victim.face === 'left') {
                                drawer.arc(x - eyeLocation, y - eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                                drawer.arc(x - eyeLocation, y + eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                            }
                            drawer.fill();
                        }
                    }
                    
                    used.push(+Date.now() - ts);

                    if (this.role.overTick === 0 && shown) {
                        drawer.fillStyle = '#0F0';
                        drawer.beginPath();
                        drawer.arc(centerX, centerY, tailHalfSize, 0, Math.PI * 2, true);
                        drawer.fill();
                        drawer.fillStyle = '#333';
                        drawer.beginPath();
                        if (this.role.face === 'up') {
                            drawer.arc(centerX - eyeLocation, centerY - eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                            drawer.arc(centerX + eyeLocation, centerY - eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                        } else if (this.role.face === 'down') {
                            drawer.arc(centerX - eyeLocation, centerY + eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                            drawer.arc(centerX + eyeLocation, centerY + eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                        } else if (this.role.face === 'right') {
                            drawer.arc(centerX + eyeLocation, centerY - eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                            drawer.arc(centerX + eyeLocation, centerY + eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                        } else if (this.role.face === 'left') {
                            drawer.arc(centerX - eyeLocation, centerY - eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                            drawer.arc(centerX - eyeLocation, centerY + eyeLocation, eyeRadius, 0, Math.PI * 2, true);
                        }
                        drawer.fill();
                    }
                    
                    used.push(+Date.now() - ts);

                    const trans = 1;
                    const smoothing = 0.5;

                    if (this.role.overTick === 0 && shown) {
                        drawer.fillStyle = `rgba(0, 0, 0, ${trans})`;
                        drawer.save();
                        let saved = true;
                        for (let y = minY, ly = lys; y < maxY; y++, ly += tailSize) {
                            const line = shown[y];

                            for (let x = minX, lx = lxs; x < maxX; x++, lx += tailSize) {
                                const around = line[x];
                                if ((around & 0x0010) === 0x0010) {
                                    if (saved) {
                                        drawer.restore();
                                        saved = false;
                                    }
                                    drawer.fillRect(lx, ly, tailSize, tailSize);
                                } else {
                                    if (!saved) {
                                        drawer.save();
                                        saved = true;
                                    }
                                    //左上角
                                    if ((around & 0x01A0) === 0x01A0) {
                                        //全黑
                                        const rg = drawer.createRadialGradient(lx + tailHalfSize, ly + tailHalfSize, 0, lx + tailHalfSize, ly + tailHalfSize, tailHalfSize);
                                        rg.addColorStop(0, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1 - smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, `rgba(0, 0, 0, ${trans})`);
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx, ly, tailHalfSize, tailHalfSize);
                                    } else if ((around & 0x0100) === 0x0100 && (around & 0x00A0) === 0) {
                                        //角黑
                                        const rg = drawer.createRadialGradient(lx, ly, 0, lx, ly, tailHalfSize);
                                        rg.addColorStop(0, `rgba(0, 0, 0, ${trans})`);
                                        rg.addColorStop(smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, 'rgba(0, 0, 0, 0)');
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx, ly, tailHalfSize, tailHalfSize);
                                    } else if ((around & 0x0020) === 0x0020) {
                                        //上黑
                                        const rg = drawer.createLinearGradient(lx, ly, lx, ly + tailHalfSize);
                                        rg.addColorStop(0, `rgba(0, 0, 0, ${trans})`);
                                        rg.addColorStop(smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, 'rgba(0, 0, 0, 0)');
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx, ly, tailHalfSize, tailHalfSize);
                                    } else if ((around & 0x0080) === 0x0080) {
                                        //左黑
                                        const rg = drawer.createLinearGradient(lx, ly, lx + tailHalfSize, ly);
                                        rg.addColorStop(0, `rgba(0, 0, 0, ${trans})`);
                                        rg.addColorStop(smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, 'rgba(0, 0, 0, 0)');
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx, ly, tailHalfSize, tailHalfSize);
                                    }
                                    //左下角
                                    if ((around & 0x00C8) === 0x00C8) {
                                        //全黑
                                        const rg = drawer.createRadialGradient(lx + tailHalfSize, ly + tailHalfSize, 0, lx + tailHalfSize, ly + tailHalfSize, tailHalfSize);
                                        rg.addColorStop(0, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1 - smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, `rgba(0, 0, 0, ${trans})`);
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx, ly + tailHalfSize, tailHalfSize, tailHalfSize);
                                    } else if ((around & 0x0040) === 0x0040 && (around & 0x0088) === 0) {
                                        //角黑
                                        const rg = drawer.createRadialGradient(lx, ly + tailSize, 0, lx, ly + tailSize, tailHalfSize);
                                        rg.addColorStop(0, `rgba(0, 0, 0, ${trans})`);
                                        rg.addColorStop(smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, 'rgba(0, 0, 0, 0)');
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx, ly + tailHalfSize, tailHalfSize, tailHalfSize);
                                    } else if ((around & 0x0008) === 0x0008) {
                                        //下黑
                                        const rg = drawer.createLinearGradient(lx, ly + tailSize, lx, ly + tailHalfSize);
                                        rg.addColorStop(0, `rgba(0, 0, 0, ${trans})`);
                                        rg.addColorStop(smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, 'rgba(0, 0, 0, 0)');
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx, ly + tailHalfSize, tailHalfSize, tailHalfSize);
                                    } else if ((around & 0x0080) === 0x0080) {
                                        //左黑
                                        const rg = drawer.createLinearGradient(lx, ly, lx + tailHalfSize, ly);
                                        rg.addColorStop(0, `rgba(0, 0, 0, ${trans})`);
                                        rg.addColorStop(smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, 'rgba(0, 0, 0, 0)');
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx, ly + tailHalfSize, tailHalfSize, tailHalfSize);
                                    }

                                    //右上角
                                    if ((around & 0x0026) === 0x0026) {
                                        //全黑
                                        const rg = drawer.createRadialGradient(lx + tailHalfSize, ly + tailHalfSize, 0, lx + tailHalfSize, ly + tailHalfSize, tailHalfSize);
                                        rg.addColorStop(0, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1 - smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, `rgba(0, 0, 0, ${trans})`);
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx + tailHalfSize, ly, tailHalfSize, tailHalfSize);
                                    } else if ((around & 0x0004) === 0x0004 && (around & 0x0022) === 0) {
                                        //角黑
                                        const rg = drawer.createRadialGradient(lx + tailSize, ly, 0, lx + tailSize, ly, tailHalfSize);
                                        rg.addColorStop(0, `rgba(0, 0, 0, ${trans})`);
                                        rg.addColorStop(smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, 'rgba(0, 0, 0, 0)');
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx + tailHalfSize, ly, tailHalfSize, tailHalfSize);
                                    } else if ((around & 0x0020) === 0x0020) {
                                        //上黑
                                        const rg = drawer.createLinearGradient(lx, ly, lx, ly + tailHalfSize);
                                        rg.addColorStop(0, `rgba(0, 0, 0, ${trans})`);
                                        rg.addColorStop(smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, 'rgba(0, 0, 0, 0)');
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx + tailHalfSize, ly, tailHalfSize, tailHalfSize);
                                    } else if ((around & 0x0002) === 0x0002) {
                                        //右黑
                                        const rg = drawer.createLinearGradient(lx + tailSize, ly, lx + tailHalfSize, ly);
                                        rg.addColorStop(0, `rgba(0, 0, 0, ${trans})`);
                                        rg.addColorStop(smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, 'rgba(0, 0, 0, 0)');
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx + tailHalfSize, ly, tailHalfSize, tailHalfSize);
                                    }
                                    //右下角
                                    if ((around & 0x000B) === 0x000B) {
                                        //全黑
                                        const rg = drawer.createRadialGradient(lx + tailHalfSize, ly + tailHalfSize, 0, lx + tailHalfSize, ly + tailHalfSize, tailHalfSize);
                                        rg.addColorStop(0, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1 - smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, `rgba(0, 0, 0, ${trans})`);
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx + tailHalfSize, ly + tailHalfSize, tailHalfSize, tailHalfSize);
                                    } else if ((around & 0x0001) === 0x0001 && (around & 0x000A) === 0) {
                                        //角黑
                                        const rg = drawer.createRadialGradient(lx + tailSize, ly + tailSize, 0, lx + tailSize, ly + tailSize, tailHalfSize);
                                        rg.addColorStop(0, `rgba(0, 0, 0, ${trans})`);
                                        rg.addColorStop(smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, 'rgba(0, 0, 0, 0)');
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx + tailHalfSize, ly + tailHalfSize, tailHalfSize, tailHalfSize);
                                    } else if ((around & 0x0008) === 0x0008) {
                                        //下黑
                                        const rg = drawer.createLinearGradient(lx, ly + tailSize, lx, ly + tailHalfSize);
                                        rg.addColorStop(0, `rgba(0, 0, 0, ${trans})`);
                                        rg.addColorStop(smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, 'rgba(0, 0, 0, 0)');
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx + tailHalfSize, ly + tailHalfSize, tailHalfSize, tailHalfSize);
                                    } else if ((around & 0x0002) === 0x0002) {
                                        //右黑
                                        const rg = drawer.createLinearGradient(lx + tailSize, ly, lx + tailHalfSize, ly);
                                        rg.addColorStop(0, `rgba(0, 0, 0, ${trans})`);
                                        rg.addColorStop(smoothing, 'rgba(0, 0, 0, 0)');
                                        rg.addColorStop(1, 'rgba(0, 0, 0, 0)');
                                        drawer.fillStyle = rg;
                                        drawer.fillRect(lx + tailHalfSize, ly + tailHalfSize, tailHalfSize, tailHalfSize);
                                    }

                                }
                            }
                        }
                    }

                    used.push(+Date.now() - ts);
                    
                    if (this.role.overTick === 0 && shown) {
                        for (const {dx, dy, x, y} of rescuers) {
                            const radius = (Math.max(tailHalfSize, Math.sqrt(dx * dx + dy * dy) * tailSize) - tailHalfSize) + tailHalfSize;
                            const rg = drawer.createRadialGradient(x, y, 0, x, y, radius);
                            rg.addColorStop(0, `rgba(255, 255, 102, ${Math.sin(this.board.tick % (config.tick.fps * config.tick.breath) / (config.tick.fps * config.tick.breath) * 2 * Math.PI) / 8 + 0.5})`);
                            rg.addColorStop(1, 'rgba(255, 255, 102, 0)');
                            drawer.fillStyle = rg;
                            drawer.beginPath();
                            drawer.arc(x, y, radius, 0, Math.PI * 2, true);
                            drawer.fill();
                        }
                    }
                    
                    used.push(+Date.now() - ts);

                    if (used[used.length - 1] > 5) {
                        console.log('used', used);
                    }
                }
                this.requireId = window.requestAnimationFrame(() => this.draw());
            },
            [[events.key.start.w, events.key.start.up, events.control.start.up]]() {
                this.moveStart('up');
            },
            [[events.key.start.d, events.key.start.right, events.control.start.right]]() {
                this.moveStart('right');
            },
            [[events.key.start.s, events.key.start.down, events.control.start.down]]() {
                this.moveStart('down');
            },
            [[events.key.start.a, events.key.start.left, events.control.start.left]]() {
                this.moveStart('left');
            },
            [[events.key.end.w, events.key.end.up, events.control.end.up]]() {
                this.moveEnd('up');
            },
            [[events.key.end.d, events.key.end.right, events.control.end.right]]() {
                this.moveEnd('right');
            },
            [[events.key.end.s, events.key.end.down, events.control.end.down]]() {
                this.moveEnd('down');
            },
            [[events.key.end.a, events.key.end.left, events.control.end.left]]() {
                this.moveEnd('left');
            },
            moveStart(direction) {
                if (!this.direction[direction]) {
                    this.direction[direction] = true;
                    this.$root.emit.moveStart(direction);
                }
            },
            moveEnd(direction) {
                if (this.direction[direction]) {
                    this.direction[direction] = false;
                    this.$root.emit.moveEnd(direction);
                }
            },
        }
    }
</script>
<style lang="less" scoped>
    .container {
        width: 100%;
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        background: #CCC;
        position: relative;
        .counter {
            width: 100vmin;
            height: 100vmin;
            background: #00000066;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 5em;
            color: red;
            position: absolute;
        }
        .canvas {
            width: 100vmin;
            height: 100vmin;
            background: #000;
        }
    }
</style>